package com.free4inno.knowledgems.service;

import com.alibaba.fastjson.JSONObject;
import com.free4inno.knowledgems.dao.ResourceDao;
import com.free4inno.knowledgems.dao.ResourceESDao;
import com.free4inno.knowledgems.domain.Resource;
import com.free4inno.knowledgems.domain.ResourceES;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.*;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.elasticsearch.index.query.QueryBuilders.multiMatchQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * Author HUYUZHU/LIYUNZE.
 * Date 2021/3/27 13:42.
 */

@Slf4j
@Service
public class ResourceEsService {

    BoolQueryBuilder reBuilder = new BoolQueryBuilder();

    @Autowired
    private ResourceESDao resourceESDao;

    @Autowired
    private ResourceDao resourceDao;

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    @Value("${spring.data.elasticsearch.cluster-nodes}")
    private String esNodes;

    // 复制索引
    public Boolean cloneIndex(String sourceIndex, String destIndex) {
        log.info(this.getClass().getName() + "----in----" + "复制索引" + "----");
        /* 使用 okhttp3 直接通过 http 操作 es */
        // set host
        String esHost = esNodes.substring(0, esNodes.indexOf(":"));
        String url = "http://" + esHost + ":9200/_reindex";
        // set body json
        Map<String, Object> jsonObject = new HashMap<>();
        jsonObject.put("source", new HashMap<String, Object>(){{put("index", sourceIndex);}});
        jsonObject.put("dest", new HashMap<String, Object>(){{put("index", destIndex);}});
        JSONObject json = new JSONObject(jsonObject);
        MediaType JSON = MediaType.parse("application/json; charset=utf-8");
        // http request
        OkHttpClient client = new OkHttpClient();
        RequestBody body = RequestBody.create(JSON, json.toString());
        Request request = new Request.Builder().url(url).post(body).build();
        try {
            Response response = client.newCall(request).execute();
            if (response.code() == 200){
                log.info(this.getClass().getName() + "----out----" + "复制成功" + "----");
                return true;
            } else {
                log.info(this.getClass().getName() + "----out----" + "复制失败" + "----");
                return false;
            }
        } catch (IOException e) {
            e.printStackTrace();
            log.info(this.getClass().getName() + "----out----" + "复制异常" + "----");
            return false;
        }
    }

    // 重建索引
    public boolean rebuildIndex(String name) {
        log.info(this.getClass().getName() + "----in----" + "重建索引" + "----");
        try {
            // 1. delete all index
            resourceESDao.deleteAll();
            // 2. get all resource in database
            List<Resource> resourcesList = resourceDao.findAll();
            // 3. add every resource into index
            for (Resource resource : resourcesList) {
                ResourceES resourceES = new ResourceES();
                resourceESDao.save(writeResourceToES(resource, resourceES));
            }
            log.info(this.getClass().getName() + "----out----" + "重建索引成功" + "----");
            return true;
        } catch (Exception e) {
            log.info(this.getClass().getName() + "----out----" + "重建索引失败" + "----");
            return false;
        }
    }

    // 删除索引
    public boolean deleteIndex(String name) {
        log.info(this.getClass().getName() + "----in----" + "删除索引" + "----");
        try {
            elasticsearchTemplate.deleteIndex(name);
            log.info(this.getClass().getName() + "----out----" + "删除索引成功" + "----");
            return true;
        } catch (Exception e) {
            log.info(this.getClass().getName() + "----out----" + "删除索引失败" + "----");
            return false;
        }
    }

    // 在ES中更新新建的资源
    public void addResourceES(int id) {
        log.info(this.getClass().getName() + "----in----" + "在ES中更新新建的资源" + "----");
        Resource resource = resourceDao.findResourceById(id);
        ResourceES resourceES = new ResourceES();
        resourceESDao.save(writeResourceToES(resource, resourceES));
        log.info(this.getClass().getName() + "----out----" + "在ES中更新新建资源完毕" + "----");
    }

    // 在ES中更新编辑过的资源
    public void updateResourceES(int id) {
        log.info(this.getClass().getName() + "----in----" + "在ES中更新编辑过的资源" + "----");
        Resource resource = resourceDao.findResourceById(id);
        ResourceES resourceES = resourceESDao.findResourceESById(id);
        resourceESDao.save(writeResourceToES(resource, resourceES));
        log.info(this.getClass().getName() + "----out----" + "在ES中更新编辑资源完毕" + "----");
    }

    public void updateResourceES() {
        log.info(this.getClass().getName() + "----in----" + "更新ES资源" + "----");
        // 获取更新周期，为了后续进行资源类型判断判断
        int period = Integer.parseInt(getPeriod());
        // log.info("周期为" + period + "秒");
        List<Resource> resourcesList = resourceDao.findAll();
        // 获取现在的时间
        LocalDateTime nowTime = LocalDateTime.now();
        // 获取上次更新时间（现在的时间减去更新周期）
        LocalDateTime lastUpdate = nowTime.minusSeconds(period);
        // 获取数据库资源中的编辑时间和新建时间
        for (Resource resource : resourcesList) {
            LocalDateTime createTime = resource.getCreateTime().toLocalDateTime();
            LocalDateTime editTime = resource.getEditTime().toLocalDateTime();
            // 判断条件为：编辑时间在上次更新之后，创建时间在上次更新之前，即为新编辑资源
            int id = resource.getId();
            if (createTime.isBefore(lastUpdate) && editTime.isAfter(lastUpdate)) {
                log.info("编辑时间在上次更新之后，创建时间在上次更新之前，即为新编辑资源"+"----");
                updateResourceES(id);
            }
            // 判断条件为：创建时间在上次更新之后,即为新增资源
            if (createTime.isAfter(lastUpdate)) {
                log.info("创建时间在上次更新之后,即为新增资源"+"----");
                addResourceES(id);
            }
        }
        log.info(this.getClass().getName() + "----out----" + "更新ES资源完毕" + "----");
    }

    // 在ES中删除资源
    public void deleteResourceES(int id) {
        log.info(this.getClass().getName() + "----in----" + "在ES中删除资源" + "----");
        try {
            ResourceES resourceES = new ResourceES();
            resourceES.setId(id);
            resourceESDao.delete(resourceES);
            log.info(this.getClass().getName() + "----out----" + "在ES中删除资源完毕" + "----");
        } catch (Exception e) {
            e.printStackTrace();
            log.error(this.getClass().getName()+"----"+"在ES中删除资源"+"----failure----"+"删除失败"+"----");
        }
    }

    // ES搜索
    public Page<ResourceES> searchResourceES(int page, int size, String query, ArrayList<String> groupIds, ArrayList<String> labelIds, int searchTimes, boolean login) {
        log.info(this.getClass().getName() + "----in----" + "ES搜索" + "----");
        Page<ResourceES> resourcePage = new PageImpl<>(new ArrayList<>());
        Sort sort = Sort.by(Sort.Direction.DESC, "create_time");
        Pageable pageable = PageRequest.of(page, size, sort);
        if (searchTimes == 1) {
            reBuilder = new BoolQueryBuilder();
        }
        if ((query == null || query.equals("")) && (groupIds == null || groupIds.isEmpty()) && (labelIds == null || labelIds.isEmpty())) {
            if (login) {
                log.info("用户已登录，查询全部资源"+"----");
                resourcePage = resourceESDao.findAll(pageable);
            } else {
                log.info("用户未登录，查询公开资源"+"----");
                resourcePage = resourceESDao.findResourceESByPermissionid("1", pageable); //未登录只能查询公开资源
            }
        } else {
            if ((query != null) && (!query.equals(""))) {
                reBuilder = reBuilder.must(multiMatchQuery(query, "title", "text"));
            }
            if ((groupIds != null) && (groupIds.size() != 0)) {
                reBuilder = reBuilder.must(termsQuery("group_id", groupIds));
            }
            if ((labelIds != null) && (labelIds.size() != 0)) {
                reBuilder = reBuilder.must(termsQuery("label_id", labelIds));
            }
            SearchQuery searchQuery;
            if (login) {
                searchQuery = new NativeSearchQueryBuilder()
                        .withQuery(reBuilder)
                        .withPageable(pageable)
                        .withMinScore(0.2f)
                        .build();
            } else {
                searchQuery = new NativeSearchQueryBuilder()
                        .withQuery(reBuilder)
                        .withFilter(termsQuery("permissionid", "1"))
                        .withPageable(pageable)
                        .withMinScore(0.2f)
                        .build();
            }//用户未登录，只能查询公开信息
            resourcePage = elasticsearchTemplate.queryForPage(searchQuery, ResourceES.class);
        }
        log.info(this.getClass().getName() + "----out----" + "返回ES查询到的resourcePage" + "----");
        return resourcePage;
    }

    // 写入Resource到ResourceES
    public ResourceES writeResourceToES(Resource resource, ResourceES resourceES) {
        log.info(this.getClass().getName() + "----in----" + "写入Resource到ResourceES" + "----");
        resourceES.setId(resource.getId());
        resourceES.setCrop_id(resource.getCropId());
        resourceES.setUser_id(resource.getUserId());
        resourceES.setCreate_time(resource.getCreateTime());
        resourceES.setEdit_time(resource.getEditTime());
        resourceES.setTitle(resource.getTitle());
        resourceES.setText(resource.getText());
        resourceES.setAttachment(resource.getAttachment());
        resourceES.setSuperior(resource.getSuperior());
        resourceES.setRecognition(resource.getRecognition());
        resourceES.setOpposition(resource.getOpposition());
        resourceES.setPageview(resource.getPageview());
        resourceES.setCollection(resource.getCollection());
        resourceES.setGroup_id(resource.getGroupId());
        resourceES.setLabel_id(resource.getLabelId());
        resourceES.setPermissionid(resource.getPermissionId().toString());
        log.info(this.getClass().getName() + "----out----" + "返回写入后的resourceES" + "----");
        return resourceES;
    }

    // 获取时间周期
    public String getPeriod() {
        log.info(this.getClass().getName() + "----in----" + "获取ES更新周期" + "----");
//        String cron = cronDao.findByCronId(1).getCron();
//        int length = cron.length();
//        return cron.substring(2, length).replaceAll("[*]", "").replaceAll("[?]", "").trim();
        //TODO:暂时写死，后续考虑计算方法
        log.info(this.getClass().getName() + "----out----" + "返回180(后端写死)" + "----");
        return "180";
    }

}
